package boardData;
import iodevice.CommandPhalanx;
import iodevice.Kdoparser;

import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

import ChessEngine.AbstractSearch;
import ChessEngine.AlphaBeta;
import ChessEngine.moveGenerator.Exceptions.BoardException;

/**
 * Hauptklasse der Engine
 * 
 * @author Fabian Hewer
 * @Version V1.0.0.001 (28.9.'11)
 */
public class MainEngine {
	//Variablen
	private Board board;
	private LinkedList commandQueue = new LinkedList();
	private static boolean finished = false;
	private boolean searching = false;
	private static AlphaBeta searcher;
	private static ParserStart starter;
	private static int moveCap = 50; //Gesammtzugzahl, mit der die Engine rechnen soll.
	
	//Konstanten
	public static final ZobirisKeyGenerator zobiGen = new ZobirisKeyGenerator();
	
	public static void main (String[] args) throws Throwable {
		try {
		MainEngine e = new MainEngine();
		e.start();
		} catch (Throwable x) {
			CommandPhalanx.infoString("Engine explodiert.");
			CommandPhalanx.infoString("Fehler aufgetreten. Bitte Engine neustarten.");
			if(starter != null) {
				starter.quit();
			}
			throw x;
		}
			
	}
	
	private void start() throws BoardException {
		starter = new ParserStart(commandQueue);
		starter.start();
		searcher = new AlphaBeta();
		while (!finished) {
			Object command = getOrder();
			if(!(command == null)){
				if(command.getClass().toString().equals("class java.lang.String")) {
					executeStringCommand((String)command);
				} else if (command.getClass().toString().equals("class [Ljava.lang.String;")) {
					executeStringArray((String[]) command);
				} else if (command.getClass().toString().equals("class [[Ljava.lang.String;")) {
					executeStringDoubleArrayCommand((String[][]) command);
				}
			}
		}
	}
	
	private void executeStringDoubleArrayCommand(String[][] command) throws BoardException {
		if (command[0][0].equals("go")){
			if(!searching){
				boolean infinitymode = false;
				boolean pondermode = false;
				boolean MTGGained = false;
				boolean TTSGained = false;
				long whiteTime = 300000;	// Vorinitialisierung damit die Engine nicht rausfliegt, 
				long blackTime = 300000;	// wenn die Gui die Zeiten nciht sendet.
				int whiteInc = 10;
				int blackInc = 10;
				int movesToGo = 50;
				long timeToSearch = 60L;
				int searchDept = 3;
				int mateIn = 0; //Matt in x Zügen.
				int nodesToSearch = 0;
				String movesToThinkAbout = "all";
				for(int i = 1; i < command.length; i++) {
					if(command[i][0] != null){
						if(command[i][0].equals("infinite")) {
							infinitymode = true;
						} else if(command[i][0].equals("ponder")) {
							pondermode = true;
						} else if(command[i][0].equals("searchmoves")) {
							movesToThinkAbout = command[i][1];
						} else if(command[i][0].equals("wtime")) {
							whiteTime = Long.parseLong(command[i][1]);
						} else if(command[i][0].equals("btime")) {
							blackTime = Long.parseLong(command[i][1]);
						} else if(command[i][0].equals("winc")) {
							whiteInc = Integer.parseInt(command[i][1]);
						} else if(command[i][0].equals("binc")) {
							blackInc = Integer.parseInt(command[i][1]);
						} else if(command[i][0].equals("movestogo")) {
							MTGGained = true;
							movesToGo = Integer.parseInt(command[i][1]);
						} else if(command[i][0].equals("dept")) {
							searchDept = Integer.parseInt(command[i][1]);
						} else if(command[i][0].equals("nodes")) {
							nodesToSearch = Integer.parseInt(command[i][1]);
						} else if(command[i][0].equals("mate")) {
							mateIn = Integer.parseInt(command[i][1]);
						} else if(command[i][0].equals("movetime")) {
							TTSGained = true;
							timeToSearch = Long.parseLong(command[i][1]);
						}
					}
				}
				//Zugzeit If Kaskade... 200 Dmg, 90 Acc.
				while(moveCap - board.getMovesDone() <= 0) {
					moveCap += 25;
				}
				if(!TTSGained)
					if(MTGGained)
						if(board.isSideWhite())
							timeToSearch = (whiteTime/movesToGo);
						else
							timeToSearch = (blackTime/movesToGo);
					else
						if(board.isSideWhite())
							timeToSearch = (whiteTime/(moveCap - board.getMovesDone()));
						else
							timeToSearch = (blackTime/(moveCap - board.getMovesDone()));
				if(timeToSearch < 1000)  //Minimalzugzeit! Zur Enginesicherheit, damit der AB nicht
				timeToSearch = 1000;    //rausfliegt.
				searcher.calculateNextMove(board, board.isSideWhite(), timeToSearch);
				searching = true;
			} else {
				CommandPhalanx.infoString("Engine sucht schon!");
			}
		}
		
	}

	private void executeStringArray(String[] command) {
		if(command[0].equals("move")){
				String bestmove = command[1];
				CommandPhalanx.bestmove(bestmove);
				moveOnBoard(bestmove);
				searching = false;
		} else {
			board = new Board(command);
		}
	}

	private void executeStringCommand(String command) {
		if(command.equals("quit")) {
			finished=true;
			CommandPhalanx.infoString("Engine Meldet sich ab");
		} else if(command.equals("board")) {
			System.out.println(board.toString());
		} else if(command.equals("ucinewgame")) {
			moveCap = 50;
		} else if(command.equals("stop")) {
			String bestmove;
			if (searching) {
				searcher.stopSearching();
				bestmove = searcher.getBestMove();
				searching = false;
			} else {
				CommandPhalanx.infoString("Engine sucht nicht");
			}
		}
	}
	
	private void moveOnBoard(String bestmove) {
		int start = board.toField(bestmove.substring(0, 2));
		int destination = board.toField(bestmove.substring(2, 4));
		char changedPiece = 'x';
		if(bestmove.length() > 4) {
			changedPiece = bestmove.toLowerCase().charAt(4);
		}
		board.move(start, destination, changedPiece);
		
	}

	private synchronized Object getOrder() {
		return commandQueue.poll();
	}

	private class ParserStart extends Thread {
		private LinkedList queue;
		private Kdoparser parser;
		
		public ParserStart(LinkedList list) {
			queue = list;
		}
		
		public void run() {
			parser = new Kdoparser();
			parser.start(queue);
		}
		
		public void quit() {
			parser.stop();
		}
		
	}
}
